/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include <stdlib.h>

#include "nfile.h"
#include "fcntl.h"
#include "ff.h"
/* typedef -------------------------------------------------------------------*/
/* define --------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables -----------------------------------------------------------------*/
int FatFs_Open(struct FileObj *file, int oflag, va_list args);
size_t FatFs_Write(struct FileObj *file, void *buf, size_t count);
size_t FatFs_Read(struct FileObj *file, void *buf, size_t count);
int FatFs_Lseek(struct FileObj *file, int offset, int whence);
int FatFs_Close(struct FileObj *file);
int FatFs_Fstat(struct FileObj *file, struct stat *status);

struct FcntlOperation fatfsOps = {
    .open = FatFs_Open,
    .write = FatFs_Write,
    .read = FatFs_Read,
    .lseek = FatFs_Lseek,
    .close = FatFs_Close,
    .fstat = FatFs_Fstat
};
/* function prototypes -------------------------------------------------------*/
int FatFs_ResultConvert(FRESULT fresult);
uint32_t FatFs_FlagConvert(uint32_t flag);
/* functions -----------------------------------------------------------------*/
/*********************************************************************************************

 * file open

**********************************************************************************************/
int FatFs_Open(struct FileObj *file, int oflag, va_list args)
{   
    FRESULT fresult;
    int result = 0;

	oflag = FatFs_FlagConvert(oflag);
	
    /* alloc memory for fatfs file obj */
    FIL *fileFatfs = (FIL *)malloc(sizeof(FIL));
    if(fileFatfs == NULL)
    {   return -105; }       // ENOMEM
    memset(fileFatfs, 0, sizeof(FIL));

    /* file open */
    fresult = f_open(fileFatfs, file->path, oflag);
    result = FatFs_ResultConvert(fresult);

    /* file open successfully, */
    if(result == 0)
    {   file->obj = fileFatfs;  }
    else
    {   free(fileFatfs);  }
    return result;  
}
/*********************************************************************************************

 * file write

**********************************************************************************************/
size_t FatFs_Write(struct FileObj *file, void *buf, size_t count)
{   
    FIL *fileFatfs = file->obj;
    size_t countWritten = 0;
    FRESULT fresult = f_write(fileFatfs, buf, count, &countWritten);

    if(countWritten > 0)
    {   return countWritten;   }
    else
    {   return FatFs_ResultConvert(fresult);    }
}
/*********************************************************************************************

 * file read

**********************************************************************************************/
size_t FatFs_Read(struct FileObj *file, void *buf, size_t count)
{   
    FIL *fileFatfs = file->obj;
    size_t countRead = 0;
    FRESULT fresult = f_read(fileFatfs, buf, count, &countRead);

    if(countRead > 0)
    {   return countRead;   }
    else
    {   return FatFs_ResultConvert(fresult);    }
}
/*********************************************************************************************

 * file lseek

**********************************************************************************************/
int FatFs_Lseek(struct FileObj *file, int offset, int whence)
{
    FIL *fileFatfs = file->obj;
    FRESULT fresult = f_lseek(fileFatfs, offset);

    return FatFs_ResultConvert(fresult); 
}
int FatFs_Close(struct FileObj *file)
{
	FIL *fileFatfs = file->obj;
    FRESULT fresult = f_close(fileFatfs);

    free(file->obj);

    return FatFs_ResultConvert(fresult); 
}
/*********************************************************************************************

 * file status

**********************************************************************************************/
int FatFs_Fstat(struct FileObj *file, struct stat *status)
{
	FIL *fileFatfs = file->obj;
	status->st_size = f_size(fileFatfs);
	
	return 0;
}

/*********************************************************************************************

 * FatFs_ResultConvert

**********************************************************************************************/
int FatFs_ResultConvert(FRESULT fresult)
{
    switch(fresult)
    {
        case FR_OK:
            return 0;
		case FR_INVALID_OBJECT:
		case FR_NO_FILE:
			return -2;
        default:
            return -5;      // EIO
    }
}
uint32_t FatFs_FlagConvert(uint32_t flag)
{
	uint32_t flagConvert = 0;
	
	if((flag & O_RDONLY) != 0)
	{	flagConvert |= FA_READ;	}
	if((flag & O_WRONLY) != 0)
	{	flagConvert |= FA_WRITE;	}
	if((flag & O_APPEND) != 0)
	{	flagConvert |= (FA_OPEN_APPEND | FA_WRITE);	}
	
	if((flagConvert & FA_OPEN_APPEND) == 0)
	{
		if((flag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
		{	flagConvert |= FA_CREATE_NEW;	}
		else if((flag & O_CREAT) != 0)
		{	flagConvert |= FA_CREATE_ALWAYS;	}
		else if((flag & O_EXCL) != 0)
		{	flagConvert |= FA_OPEN_EXISTING;	}
	}
	
	return flagConvert;
}
FILE_DRIVER_INSTALL(fatfs);
